#pragma once
#include <lcir/ADT/IListIterator.h>
#include <lcir/ADT/ListNode.h>
#include <stdexcept>
namespace lcir {
namespace ilist::detail {
/// 萃取器，判断一个链表是否掌管节点的生命周期
/// 如果不为ref，节点随链表erase而销毁
/// TODO: 如果NodeTy是数组。。。
template <class NodeTy, bool IsRef>
struct IListNodeRefTrait;
template <class NodeTy>
struct IListNodeRefTrait<NodeTy, false> {
  static void deleteNode(NodeTy *node) { delete node; }
};
template <class NodeTy>
struct IListNodeRefTrait<NodeTy, true> {
  static void deleteNode(NodeTy *node) {}
};
}  // namespace ilist::detail
// 注意，这个链表会掌管节点的生命周期!
template <class NodeTy, bool IsRef>
class IListBase {
 public:
  using type = NodeTy;
  using iterator = IListIterator<NodeTy, false, false>;
  using constIterator = IListIterator<NodeTy, false, true>;
  using reverseIterator = IListIterator<NodeTy, true, false>;
  using constReverseIterator = IListIterator<NodeTy, true, true>;
  using trait = listnode::detail::Trait<NodeTy, false>;
  using baseNodePtr = typename trait::nodePointer;
  using refTrait = typename ilist::detail::IListNodeRefTrait<NodeTy, IsRef>;

 private:
  EndNode<NodeTy> endNode;
  void check(baseNodePtr node) {
    [[unlikely]]if(node->inserted())
    throw std::runtime_error("list node have inserted into list!");
  }
  
  void resetNode(baseNodePtr node) {
    node->setNext(nullptr);
    node->setPrev(nullptr);
  }
  void insertBase(baseNodePtr insertPoint, baseNodePtr insertNode) {
    check(insertNode);
    auto *prev = insertPoint->getPrev();
    prev->setNext(insertNode);
    insertNode->setPrev(prev);
    insertPoint->setPrev(insertNode);
    insertNode->setNext(insertPoint);
  }
  void insertAfterBase(baseNodePtr insertPoint, baseNodePtr insertNode) {
    check(insertNode);
    auto *next = insertPoint->getNext();
    next->setPrev(insertNode);
    insertNode->setNext(next);
    insertPoint->setNext(insertNode);
    insertNode->setPrev(insertPoint);
  }

 public:
  explicit IListBase() = default;

void insert(NodeTy *insertPoint, NodeTy *insertNode) {
    insertBase(insertPoint, insertNode);
  }
  void insertAfter(NodeTy *insertPoint, NodeTy *insertNode) {
    insertAfterBase(insertPoint, insertNode);
  }

  void insert(iterator insertPoint, iterator insertNode) {
    insertBase(insertPoint.operator ->(), insertNode.operator ->());
  }
  void insertAfter(iterator insertPoint, iterator insertNode) {
    insertAfterBase(insertPoint.operator ->(), insertNode.operator ->());
  }

  void pushBack(NodeTy *node) { insertBase(&endNode, node); }
  void pushFront(NodeTy *node) { insertAfterBase(&endNode, node); }
  void popBack() { erase(back()); }
  void popFront() { erase(begin()); }

  bool empty() const{ return endNode.empty(); }
  void clear() {
    if constexpr (!IsRef)
      erase(begin(), back());
    else
      endNode.reset();
  }

  NodeTy *remove(iterator it) {
    remove(it.getNode());
  }

  NodeTy *remove(NodeTy *node) {
    auto *prev = node->getPrev();
    auto *next = node->getNext();
    prev->setNext(next);
    next->setPrev(prev);
    resetNode(node);
    return node;
  }

  void erase(NodeTy *node) {
    remove(node);
    refTrait::deleteNode(node);
  }

  void erase(iterator it) {
    auto *node = it.getNode();
    erase(node);
  }

  void erase(iterator begin, iterator end) {
    for (auto it = begin; it != end;) {
      auto *node = it.getNode();
      it++;
      erase(node);
    }
  }

  void erase(NodeTy *begin, NodeTy *end) {
    erase(iterator(begin), iterator(end));
  }

  void getNext(NodeTy *node) const {
    node->getNext() == endNode() ? nullptr : node->getNext();
  }
  void getPrev(NodeTy *node) const {
    node->getPrev() == endNode() ? nullptr : node->getPrev();
  }

  iterator back() {return --iterator(endNode);}
  constIterator back() const {return --constIterator(endNode);}

  iterator begin() { return ++iterator(endNode); };
  iterator end() { return iterator(endNode); };
  constIterator begin() const { return ++constIterator(endNode); };
  constIterator end() const { return constIterator(endNode); };
  reverseIterator rbegin() { return ++reverseIterator(endNode); };
  reverseIterator rend() { return reverseIterator(endNode); };
  constReverseIterator rbegin() const { return ++constReverseIterator(endNode); };
  constReverseIterator rend() const { return constReverseIterator(endNode); };
  virtual ~IListBase() {
    clear();
  }
};
template <class NodeTy>
class IList : public IListBase<NodeTy, false> {};
template <class NodeTy>
class IListRef : public IListBase<NodeTy, true> {};
}  // namespace lcir